// Copyright (c) 2015-2018, Satoshi Tanda. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

/// @file
/// Implements DdiMon functions.

#include "ddi_mon.h"
#include <ntimage.h>
#define NTSTRSAFE_NO_CB_FUNCTIONS
#include <ntstrsafe.h>
#include "../HyperPlatform/HyperPlatform/common.h"
#include "../HyperPlatform/HyperPlatform/log.h"
#include "../HyperPlatform/HyperPlatform/util.h"
#include "../HyperPlatform/HyperPlatform/ept.h"
#undef _HAS_EXCEPTIONS
#define _HAS_EXCEPTIONS 0
#include <array>
#include "shadow_hook.h"

////////////////////////////////////////////////////////////////////////////////
//
// macro utilities
//

////////////////////////////////////////////////////////////////////////////////
//
// constants and macros
//

////////////////////////////////////////////////////////////////////////////////
//
// types
//

// A helper type for parsing a PoolTag value
union PoolTag {
  ULONG value;
  UCHAR chars[4];
};

// A callback type for EnumExportedSymbols()
using EnumExportedSymbolsCallbackType = bool (*)(
    ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory,
    ULONG_PTR directory_base, ULONG_PTR directory_end, void* context);

// For SystemProcessInformation
enum SystemInformationClass {
  kSystemProcessInformation = 5,
};

// For NtQuerySystemInformation
struct SystemProcessInformation {
  ULONG next_entry_offset;
  ULONG number_of_threads;
  LARGE_INTEGER working_set_private_size;
  ULONG hard_fault_count;
  ULONG number_of_threads_high_watermark;
  ULONG64 cycle_time;
  LARGE_INTEGER create_time;
  LARGE_INTEGER user_time;
  LARGE_INTEGER kernel_time;
  UNICODE_STRING image_name;
  // omitted. see ole32!_SYSTEM_PROCESS_INFORMATION
};

////////////////////////////////////////////////////////////////////////////////
//
// prototypes
//

_IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C
    static void DdimonpFreeAllocatedTrampolineRegions();

_IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C static NTSTATUS
    DdimonpEnumExportedSymbols(_In_ ULONG_PTR base_address,
                               _In_ EnumExportedSymbolsCallbackType callback,
                               _In_opt_ void* context);

_IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C
    static bool DdimonpEnumExportedSymbolsCallback(
        _In_ ULONG index, _In_ ULONG_PTR base_address,
        _In_ PIMAGE_EXPORT_DIRECTORY directory, _In_ ULONG_PTR directory_base,
        _In_ ULONG_PTR directory_end, _In_opt_ void* context);

static std::array<char, 5> DdimonpTagToString(_In_ ULONG tag_value);

template <typename T>
static T DdimonpFindOrignal(_In_ T handler);

static VOID DdimonpHandleExQueueWorkItem(_Inout_ PWORK_QUEUE_ITEM work_item,
                                         _In_ WORK_QUEUE_TYPE queue_type);

static PVOID DdimonpHandleExAllocatePoolWithTag(_In_ POOL_TYPE pool_type,
                                                _In_ SIZE_T number_of_bytes,
                                                _In_ ULONG tag);

static VOID DdimonpHandleExFreePool(_Pre_notnull_ PVOID p);

static VOID DdimonpHandleExFreePoolWithTag(_Pre_notnull_ PVOID p,
                                           _In_ ULONG tag);

static NTSTATUS DdimonpHandleNtQuerySystemInformation(
    _In_ SystemInformationClass SystemInformationClass,
    _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength,
    _Out_opt_ PULONG ReturnLength);

static NTSTATUS DdimonpHandleNtCreateFile(
    _Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes,
    _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition,
    _In_ ULONG CreateOptions, _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
    _In_ ULONG EaLength);
static NTSTATUS DdimonpHandleNtWriteVirtualMemory(
    _In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress,
    _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ SIZE_T BufferSize,
    _Out_opt_ PSIZE_T NumberOfBytesWritten);
static NTSTATUS DdimonpHandleNtReadVirtualMemory(
    _In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress,
    _Out_writes_bytes_(BufferSize) PVOID Buffer, _In_ SIZE_T BufferSize,
    _Out_opt_ PSIZE_T NumberOfBytesRead);
static NTSTATUS DdimonpHandleNtCreateProcess(
    _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_ HANDLE ParentProcess,
    _In_ BOOLEAN InheritObjectTable, _In_opt_ HANDLE SectionHandle,
    _In_opt_ HANDLE DebugPort, _In_opt_ HANDLE ExceptionPort);
static NTSTATUS NTAPI DdimonpHandleNtOpenProcess(
    _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId);
static NTSTATUS DdimonpHandleMmCopyVirtualMemory(
    PEPROCESS SourceProcess, PVOID SourceAddress, PEPROCESS TargetProcess,
    PVOID TargetAddress, SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode,
    PSIZE_T ReturnSize);

static NTSTATUS DdimonpHandleNtCreateSection(
    _Out_ PHANDLE SectionHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
    _In_opt_ PLARGE_INTEGER MaximumSize, _In_ ULONG SectionPageProtection,
    _In_ ULONG AllocationAttributes, _In_opt_ HANDLE FileHandle);

static NTSTATUS DdimonpHandleNtQueryInformationToken(
    _In_ HANDLE TokenHandle, _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
    _Out_writes_bytes_(TokenInformationLength) PVOID TokenInformation,
    _In_ ULONG TokenInformationLength, _Out_ PULONG ReturnLength);
static NTSTATUS DdimonpHandleNtSetInformationToken(
    _In_ HANDLE TokenHandle, _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
    _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
    _In_ ULONG TokenInformationLength);

static NTSTATUS DdimonpHandleNtWriteFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_reads_bytes_(Length) PVOID Buffer,
    _In_ ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key);

static NTSTATUS DdimonpHandleCmRegisterCallback(
    IN PEX_CALLBACK_FUNCTION Function, IN PVOID Context,
    IN OUT PLARGE_INTEGER Cookie);
static NTSTATUS DdimonpHandleCmRegisterCallbackEx(
    IN PEX_CALLBACK_FUNCTION Function, IN PCUNICODE_STRING Altitude,
    IN PVOID Driver, IN PVOID Context, OUT PLARGE_INTEGER Cookie,
    PVOID Reserved);
static NTSTATUS DdimonpHandleNtDeleteFile(
    _In_ POBJECT_ATTRIBUTES ObjectAttributes);

static NTSTATUS DdimonpHandleNtQueryDirectoryFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID FileInformation, _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass,
    _In_ BOOLEAN ReturnSingleEntry, _In_opt_ PUNICODE_STRING FileName,
    _In_ BOOLEAN RestartScan);

static NTSTATUS DdimonpHandleNtReadFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length,
    _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key);

static NTSTATUS DdimonpHandleNtSetInformationFile(
    _In_ HANDLE FileHandle, _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_reads_bytes_(Length) PVOID FileInformation, _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass);

static NTSTATUS DdimonpHandleNtDeviceIoControlFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_ ULONG IoControlCode,
    _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
    _In_ ULONG InputBufferLength,
    _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
    _In_ ULONG OutputBufferLength);

static NTSTATUS DdimonpHandleNtAdjustPrivilegesToken(
    _In_ HANDLE TokenHandle, _In_ BOOLEAN DisableAllPrivileges,
    _In_opt_ PTOKEN_PRIVILEGES NewState, _In_ ULONG BufferLength,
    _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength)
        PTOKEN_PRIVILEGES PreviousState,
    _Out_ _When_(PreviousState == NULL, _Out_opt_) PULONG ReturnLength);
static NTSTATUS DdimonpHandleNtDuplicateToken(
    _In_ HANDLE ExistingTokenHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_ BOOLEAN EffectiveOnly,
    _In_ TOKEN_TYPE TokenType, _Out_ PHANDLE NewTokenHandle);
static NTSTATUS DdimonpHandleNtAllocateVirtualMemory(
    _In_ HANDLE ProcessHandle,
    _Inout_ _At_(*BaseAddress,
                 _Readable_bytes_(*RegionSize) _Writable_bytes_(*RegionSize)
                     _Post_readable_byte_size_(*RegionSize)) PVOID* BaseAddress,
    _In_ ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize,
    _In_ ULONG AllocationType, _In_ ULONG Protect);

static NTSTATUS DdimonpHandleNtOpenProcessToken(_In_ HANDLE ProcessHandle,
                                                _In_ ACCESS_MASK DesiredAccess,
                                                _Out_ PHANDLE TokenHandle);
static NTSTATUS DdimonpHandleNtOpenThreadToken(_In_ HANDLE ThreadHandle,
                                               _In_ ACCESS_MASK DesiredAccess,
                                               _In_ BOOLEAN OpenAsSelf,
                                               _Out_ PHANDLE TokenHandle);
static NTSTATUS DdimonpHandleRtlCreateRegistryKey(_In_ ULONG RelativeTo,
                                                  _In_ PWSTR Path);
static NTSTATUS DdimonpHandleRtlQueryRegistryValuesEx(
    _In_ ULONG RelativeTo, _In_ PWSTR Path,
    _In_ PRTL_QUERY_REGISTRY_TABLE QueryTable, _In_ PVOID Context,
    _In_opt_ PVOID Environment);
static NTSTATUS DdimonpHandleRtlWriteRegistryValue(
    _In_ ULONG RelativeTo, _In_ PWSTR Path, _In_ PWSTR ValueName,
    _In_ ULONG ValueType, _In_ PVOID ValueData, _In_ ULONG ValueLength);
static NTSTATUS DdimonpHandleRtlDeleteRegistryValue(_In_ ULONG RelativeTo,
                                                    _In_ PWSTR Path,
                                                    _In_ PWSTR ValueName);
static NTSTATUS DdimonpHandleRtlRtlCheckRegistryKey(_In_ ULONG RelativeTo,
                                                    _In_ PWSTR Path);
static NTSTATUS DdimonpHandlePsCreateSystemThread(
    PHANDLE ThreadHandle, ULONG DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
    PCLIENT_ID ClientId, PKSTART_ROUTINE StartRoutine, PVOID StartContext);

static NTSTATUS DdimonpHandlePsSetContextThread(
    __in PETHREAD Thread, __in PCONTEXT ThreadContext,
    __in KPROCESSOR_MODE PreviousMode);
static NTSTATUS DdimonpHandlePsSuspendProcess(PEPROCESS pEProcess);
static NTSTATUS DdimonpHandlePsResumeProcess(PEPROCESS pEProcess);
static BOOLEAN DdimonpHandleSeAccessCheck(
    PSECURITY_DESCRIPTOR SecurityDescriptor,
    PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
    BOOLEAN SubjectContextLocked, ACCESS_MASK DesiredAccess,
    ACCESS_MASK PreviouslyGrantedAccess, PPRIVILEGE_SET* Privileges,
    PGENERIC_MAPPING GenericMapping, KPROCESSOR_MODE AccessMode,
    PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus);

static NTSTATUS DdimonpHandleRtlDecompressBuffer(
    _In_ USHORT CompressionFormat,
    _Out_writes_bytes_to_(UncompressedBufferSize, *FinalUncompressedSize)
        PUCHAR UncompressedBuffer,
    _In_ ULONG UncompressedBufferSize,
    _In_reads_bytes_(CompressedBufferSize) PUCHAR CompressedBuffer,
    _In_ ULONG CompressedBufferSize, _Out_ PULONG FinalUncompressedSize);
static NTSTATUS DdimonpHandleRtlDecompressBufferEx(
    _In_ USHORT CompressionFormat,
    _Out_writes_bytes_to_(UncompressedBufferSize, *FinalUncompressedSize)
        PUCHAR UncompressedBuffer,
    _In_ ULONG UncompressedBufferSize,
    _In_reads_bytes_(CompressedBufferSize) PUCHAR CompressedBuffer,
    _In_ ULONG CompressedBufferSize, _Out_ PULONG FinalUncompressedSize,
    _In_ PVOID WorkSpace);
static NTSTATUS DdimonpHandleIoCreateSymbolicLink(
    PUNICODE_STRING SymbolicLinkName, PUNICODE_STRING DeviceName);
static NTSTATUS DdimonpHandleRtlAnsiStringToUnicodeString(
    PUNICODE_STRING DestinationString, PCANSI_STRING SourceString,
    BOOLEAN AllocateDestinationString);

static NTSTATUS DdimonpHandleRtlUnicodeStringToAnsiString(
    PANSI_STRING DestinationString, PCUNICODE_STRING SourceString,
    BOOLEAN AllocateDestinationString);
static VOID DdimonpHandleRtlInitUnicodeString(
    PUNICODE_STRING DestinationString, __drv_aliasesMem PCWSTR SourceString);
static LONG DdimonpHandleRtlCompareUnicodeString(PCUNICODE_STRING String1,
                                                 PCUNICODE_STRING String2,
                                                 BOOLEAN CaseInSensitive);
static VOID DdimonpHandleRtlInitAnsiString(PANSI_STRING DestinationString,
                                           __drv_aliasesMem PCSZ SourceString);
static void DdimonpHandleRtlCopyMemory(void* Destination, void* Source,
                                       size_t Length);
#if defined(ALLOC_PRAGMA)
#pragma alloc_text(PAGE, DdimonInitialization)
#pragma alloc_text(PAGE, DdimonpEnumExportedSymbols)
#pragma alloc_text(PAGE, DdimonpEnumExportedSymbolsCallback)
#pragma alloc_text(PAGE, DdimonTermination)
#pragma alloc_text(PAGE, DdimonpFreeAllocatedTrampolineRegions)
#endif

////////////////////////////////////////////////////////////////////////////////
//
// variables
//

// Defines where to install shadow hooks and their handlers
//
// Because of simplified implementation of DdiMon, DdiMon is unable to handle
// any of following exports properly:
//  - already unmapped exports (eg, ones on the INIT section) because it no
//    longer exists on memory
//  - exported data because setting 0xcc does not make any sense in this case
//  - functions does not comply x64 calling conventions, for example Zw*
//    functions. Because contents of stack do not hold expected values leading
//    handlers to failure of parameter analysis that may result in bug check.
//
// Also the following care should be taken:
//  - Function parameters may be an user-address space pointer and not
//    trusted. Even a kernel-address space pointer should not be trusted for
//    production level security. Verity and capture all contents from user
//    supplied address to VMM, then use them.
static ShadowHookTarget g_ddimonp_hook_targets[] = {
    {
        RTL_CONSTANT_STRING(L"EXQUEUEWORKITEM"),
        DdimonpHandleExQueueWorkItem,
        nullptr,
    },
    {
        RTL_CONSTANT_STRING(L"EXALLOCATEPOOLWITHTAG"),
        DdimonpHandleExAllocatePoolWithTag,
        nullptr,
    },
    {
        RTL_CONSTANT_STRING(L"EXFREEPOOL"),
        DdimonpHandleExFreePool,
        nullptr,
    },
    {
        RTL_CONSTANT_STRING(L"EXFREEPOOLWITHTAG"),
        DdimonpHandleExFreePoolWithTag,
        nullptr,
    },
    {
        RTL_CONSTANT_STRING(L"NTQUERYSYSTEMINFORMATION"),
        DdimonpHandleNtQuerySystemInformation,
        nullptr,
    },
    {
        RTL_CONSTANT_STRING(L"NTCREATEFILE"),
        DdimonpHandleNtCreateFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTOPENPROCESS"),
        DdimonpHandleNtOpenProcess,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTREADVIRTUALMEMORY"),
        DdimonpHandleNtCreateProcess,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"MMCOPYVIRTUALMEMORY"),
        DdimonpHandleMmCopyVirtualMemory,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTCREATESECTION"),
        DdimonpHandleNtCreateSection,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTQUERYINFORMATIONTOKEN"),
        DdimonpHandleNtQueryInformationToken,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTSETINFORMATIONTOKEN"),
        DdimonpHandleNtSetInformationToken,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"CMREGISTERCALLBACKEX"),
        DdimonpHandleCmRegisterCallbackEx,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"CMREGISTERCALLBACK"),
        DdimonpHandleCmRegisterCallback,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTWRITEFILE"),
        DdimonpHandleNtWriteFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTDELETEFILE"),
        DdimonpHandleNtDeleteFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTQUERYDIRECTORYFILE"),
        DdimonpHandleNtQueryDirectoryFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTREADFILE"),
        DdimonpHandleNtReadFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTSETINFORMATIONFILE"),
        DdimonpHandleNtSetInformationFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTDEVICEIOCONTROLFILE"),
        DdimonpHandleNtDeviceIoControlFile,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTADJUSTPRIVILEGESTOKEN"),
        DdimonpHandleNtAdjustPrivilegesToken,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTDUPLICATETOKEN"),
        DdimonpHandleNtDuplicateToken,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTALLOCATEVIRTUALMEMORY"),
        DdimonpHandleNtAllocateVirtualMemory,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTOPENPROCESSTOKEN"),
        DdimonpHandleNtOpenProcessToken,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"NTOPENTHREADTOKEN"),
        DdimonpHandleNtOpenThreadToken,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLCREATEREGISTRYKEY"),
        DdimonpHandleRtlCreateRegistryKey,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLQUERYREGISTRYVALUESEX"),
        DdimonpHandleRtlQueryRegistryValuesEx,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLWRITEREGISTRYVALUE"),
        DdimonpHandleRtlWriteRegistryValue,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLDELETEREGISTRYVALUE"),
        DdimonpHandleRtlDeleteRegistryValue,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLCHECKREGISTRYKEY"),
        DdimonpHandleRtlRtlCheckRegistryKey,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"PSCREATESYSTEMTHREAD"),
        DdimonpHandlePsCreateSystemThread,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"PSSETCONTEXTTHREAD"),
        DdimonpHandlePsSetContextThread,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"PSSUSPENDPROCESS"),
        DdimonpHandlePsSuspendProcess,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"PSRESUMEPROCESS"),
        DdimonpHandlePsResumeProcess,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"SEACCESSCHECK"),
        DdimonpHandleSeAccessCheck,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLDECOMPRESSBUFFER"),
        DdimonpHandleRtlDecompressBuffer,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLDECOMPRESSBUFFEREX"),
        DdimonpHandleRtlDecompressBufferEx,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"IOCREATESYMBOLICLINK"),
        DdimonpHandleIoCreateSymbolicLink,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLANSISTRINGTOUNICODESTRING"),
        DdimonpHandleRtlAnsiStringToUnicodeString,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLUNICODESTRINGTOANSISTRING"),
        DdimonpHandleRtlUnicodeStringToAnsiString,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLINITUNICODESTRING"),
        DdimonpHandleRtlInitUnicodeString,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLCOMPAREUNICODESTRING"),
        DdimonpHandleRtlCompareUnicodeString,
        nullptr,

    },
    {
        RTL_CONSTANT_STRING(L"RTLINITANSISTRING"),
        DdimonpHandleRtlInitAnsiString,
        nullptr,

    },
};

////////////////////////////////////////////////////////////////////////////////
//
// implementations
//

// Initializes DdiMon
_Use_decl_annotations_ EXTERN_C NTSTATUS
DdimonInitialization(SharedShadowHookData* shared_sh_data) {
  // Get a base address of ntoskrnl
  auto nt_base = UtilPcToFileHeader(KdDebuggerEnabled);
  if (!nt_base) {
    return STATUS_UNSUCCESSFUL;
  }

  // Install hooks by enumerating exports of ntoskrnl, but not activate them yet
  auto status = DdimonpEnumExportedSymbols(reinterpret_cast<ULONG_PTR>(nt_base),
                                           DdimonpEnumExportedSymbolsCallback,
                                           shared_sh_data);
  if (!NT_SUCCESS(status)) {
    return status;
  }

  // Activate installed hooks
  status = ShEnableHooks();
  if (!NT_SUCCESS(status)) {
    DdimonpFreeAllocatedTrampolineRegions();
    return status;
  }

  HYPERPLATFORM_LOG_INFO("DdiMon has been initialized.");
  return status;
}

// Terminates DdiMon
_Use_decl_annotations_ EXTERN_C void DdimonTermination() {
  PAGED_CODE();

  ShDisableHooks();
  UtilSleep(1000);
  DdimonpFreeAllocatedTrampolineRegions();
  HYPERPLATFORM_LOG_INFO("DdiMon has been terminated.");
}

// Frees trampoline code allocated and stored in g_ddimonp_hook_targets by
// DdimonpEnumExportedSymbolsCallback()
_Use_decl_annotations_ EXTERN_C static void
DdimonpFreeAllocatedTrampolineRegions() {
  PAGED_CODE();

  for (auto& target : g_ddimonp_hook_targets) {
    if (target.original_call) {
      ExFreePoolWithTag(target.original_call, kHyperPlatformCommonPoolTag);
      target.original_call = nullptr;
    }
  }
}

// Enumerates all exports in a module specified by base_address.
_Use_decl_annotations_ EXTERN_C static NTSTATUS DdimonpEnumExportedSymbols(
    ULONG_PTR base_address, EnumExportedSymbolsCallbackType callback,
    void* context) {
  PAGED_CODE();

  auto dos = reinterpret_cast<PIMAGE_DOS_HEADER>(base_address);
  auto nt = reinterpret_cast<PIMAGE_NT_HEADERS>(base_address + dos->e_lfanew);
  auto dir = reinterpret_cast<PIMAGE_DATA_DIRECTORY>(
      &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
  if (!dir->Size || !dir->VirtualAddress) {
    return STATUS_SUCCESS;
  }

  auto dir_base = base_address + dir->VirtualAddress;
  auto dir_end = base_address + dir->VirtualAddress + dir->Size - 1;
  auto exp_dir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(base_address +
                                                           dir->VirtualAddress);
  for (auto i = 0ul; i < exp_dir->NumberOfNames; i++) {
    if (!callback(i, base_address, exp_dir, dir_base, dir_end, context)) {
      return STATUS_SUCCESS;
    }
  }
  return STATUS_SUCCESS;
}

// Checks if the export is listed as a hook target, and if so install a hook.
_Use_decl_annotations_ EXTERN_C static bool DdimonpEnumExportedSymbolsCallback(
    ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory,
    ULONG_PTR directory_base, ULONG_PTR directory_end, void* context) {
  PAGED_CODE();

  if (!context) {
    return false;
  }

  auto functions =
      reinterpret_cast<ULONG*>(base_address + directory->AddressOfFunctions);
  auto ordinals = reinterpret_cast<USHORT*>(base_address +
                                            directory->AddressOfNameOrdinals);
  auto names =
      reinterpret_cast<ULONG*>(base_address + directory->AddressOfNames);

  auto ord = ordinals[index];
  auto export_address = base_address + functions[ord];
  auto export_name = reinterpret_cast<const char*>(base_address + names[index]);

  // Check if an export is forwarded one? If so, ignore it.
  if (UtilIsInBounds(export_address, directory_base, directory_end)) {
    return true;
  }

  // convert the name to UNICODE_STRING
  wchar_t name[100];
  auto status =
      RtlStringCchPrintfW(name, RTL_NUMBER_OF(name), L"%S", export_name);
  if (!NT_SUCCESS(status)) {
    return true;
  }
  UNICODE_STRING name_u = {};
  RtlInitUnicodeString(&name_u, name);
  // HYPERPLATFORM_LOG_INFO_SAFE("names: %S",name_u.Buffer);

  for (auto& target : g_ddimonp_hook_targets) {
    // Is this export listed as a target
    if (!FsRtlIsNameInExpression(&target.target_name, &name_u, TRUE, nullptr)) {
      continue;
    }

    // Yes, install a hook to the export
    if (!ShInstallHook(reinterpret_cast<SharedShadowHookData*>(context),
                       reinterpret_cast<void*>(export_address), &target)) {
      // This is an error which should not happen
      DdimonpFreeAllocatedTrampolineRegions();
      return false;
    }
    HYPERPLATFORM_LOG_INFO("Hook has been installed at %016Ix %s.",
                           export_address, export_name);
  }
  return true;
}

// Converts a pool tag in integer to a printable string
_Use_decl_annotations_ static std::array<char, 5> DdimonpTagToString(
    ULONG tag_value) {
  PoolTag tag = {tag_value};
  for (auto& c : tag.chars) {
    if (!c && isspace(c)) {
      c = ' ';
    }
    if (!isprint(c)) {
      c = '.';
    }
  }

  std::array<char, 5> str;
  auto status =
      RtlStringCchPrintfA(str.data(), str.size(), "%c%c%c%c", tag.chars[0],
                          tag.chars[1], tag.chars[2], tag.chars[3]);
  NT_VERIFY(NT_SUCCESS(status));
  return str;
}

// Finds a handler to call an original function
template <typename T>
static T DdimonpFindOrignal(T handler) {
  for (const auto& target : g_ddimonp_hook_targets) {
    if (target.handler == handler) {
      NT_ASSERT(target.original_call);
      return reinterpret_cast<T>(target.original_call);
    }
  }
  NT_ASSERT(false);
  return nullptr;
}

// The hook handler for ExFreePool(). Logs if ExFreePool() is called from where
// not backed by any image
_Use_decl_annotations_ static VOID DdimonpHandleExFreePool(PVOID p) {
  const auto original = DdimonpFindOrignal(DdimonpHandleExFreePool);
  original(p);

  // Is inside image?
  auto return_addr = _ReturnAddress();
  if (UtilPcToFileHeader(return_addr)) {
    return;
  }

  HYPERPLATFORM_LOG_INFO_SAFE("%p: ExFreePool(P= %p)", return_addr, p);
}

// The hook handler for ExFreePoolWithTag(). Logs if ExFreePoolWithTag() is
// called from where not backed by any image.
_Use_decl_annotations_ static VOID DdimonpHandleExFreePoolWithTag(PVOID p,
                                                                  ULONG tag) {
  const auto original = DdimonpFindOrignal(DdimonpHandleExFreePoolWithTag);
  original(p, tag);

  // Is inside image?
  auto return_addr = _ReturnAddress();
  if (UtilPcToFileHeader(return_addr)) {
    return;
  }

  HYPERPLATFORM_LOG_INFO_SAFE("%p: ExFreePoolWithTag(P= %p, Tag= %s)",
                              return_addr, p, DdimonpTagToString(tag).data());
}

// The hook handler for ExQueueWorkItem(). Logs if a WorkerRoutine points to
// where not backed by any image.
_Use_decl_annotations_ static VOID DdimonpHandleExQueueWorkItem(
    PWORK_QUEUE_ITEM work_item, WORK_QUEUE_TYPE queue_type) {
  const auto original = DdimonpFindOrignal(DdimonpHandleExQueueWorkItem);

  // Is inside image?
  if (UtilPcToFileHeader(work_item->WorkerRoutine)) {
    // Call an original after checking parameters. It is common that a work
    // routine frees a work_item object resulting in wrong analysis.
    original(work_item, queue_type);
    return;
  }

  auto return_addr = _ReturnAddress();
  HYPERPLATFORM_LOG_INFO_SAFE(
      "%p: ExQueueWorkItem({Routine= %p, Parameter= %p}, %d)", return_addr,
      work_item->WorkerRoutine, work_item->Parameter, queue_type);

  original(work_item, queue_type);
}

// The hook handler for ExAllocatePoolWithTag(). Logs if ExAllocatePoolWithTag()
// is called from where not backed by any image.
_Use_decl_annotations_ static PVOID DdimonpHandleExAllocatePoolWithTag(
    POOL_TYPE pool_type, SIZE_T number_of_bytes, ULONG tag) {
  const auto original = DdimonpFindOrignal(DdimonpHandleExAllocatePoolWithTag);
  const auto result = original(pool_type, number_of_bytes, tag);

  // Is inside image?
  auto return_addr = _ReturnAddress();
  if (UtilPcToFileHeader(return_addr)) {
    return result;
  }

  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtCreateFile,Tag=%s",
                              DdimonpTagToString(tag).data());
  return result;
}

// The hook handler for NtQuerySystemInformation(). Removes an entry for cmd.exe
// and hides it from being listed.
_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtQuerySystemInformation(
    SystemInformationClass system_information_class, PVOID system_information,
    ULONG system_information_length, PULONG return_length) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleNtQuerySystemInformation);
  const auto result = original(system_information_class, system_information,
                               system_information_length, return_length);
  if (!NT_SUCCESS(result)) {
    return result;
  }
  if (system_information_class != kSystemProcessInformation) {
    return result;
  }

  auto next = reinterpret_cast<SystemProcessInformation*>(system_information);
  while (next->next_entry_offset) {
    auto curr = next;
    next = reinterpret_cast<SystemProcessInformation*>(
        reinterpret_cast<UCHAR*>(curr) + curr->next_entry_offset);
    if ((_wcsnicmp(next->image_name.Buffer, L"cmd.exe", 7) == 0) || (_wcsnicmp(next->image_name.Buffer, L"dumpcap.exe", 11) == 0) || 
		(_wcsnicmp(next->image_name.Buffer, L"powershell.exe", 14) == 0) ||
        (_wcsnicmp(next->image_name.Buffer, L"python.exe", 10) == 0)|| 
		(_wcsnicmp(next->image_name.Buffer, L"tshark.exe", 10) == 0)) {
      if (next->next_entry_offset) {
        curr->next_entry_offset += next->next_entry_offset;
      } else {
        curr->next_entry_offset = 0;
      }
      next = curr;
    }
  }
  return result;
}

// The hook handler for DdimonpHandleNtCreateFile().
_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtCreateFile(
    PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
    PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess,
    ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer,
    ULONG EaLength) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtCreateFile);
  const auto result =
      original(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
               AllocationSize, FileAttributes, ShareAccess, CreateDisposition,
               CreateOptions, EaBuffer, EaLength);

  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtCreateFile,File=%wZ,AllocationSize=%08Ix",
      ObjectAttributes->ObjectName, AllocationSize);
  return result;
}

// The hook handler for DdimonpHandleNtWriteVirtualMemory().
_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtWriteVirtualMemory(
    _In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress,
    _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ SIZE_T BufferSize,
    _Out_opt_ PSIZE_T NumberOfBytesWritten) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtWriteVirtualMemory);
  const auto result = original(ProcessHandle, BaseAddress, Buffer, BufferSize,
                               NumberOfBytesWritten);
  HYPERPLATFORM_LOG_INFO_SAFE("hello");
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtWriteVirtualMemory,ProcessHandle=%08x,"
      "NumberOfBytesWritten=%08Ix,Bytes=0x%p\n",
      ProcessHandle, NumberOfBytesWritten, Buffer);
  return result;
}

//_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtReadVirtualMemory(
//    _In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress,
//    _Out_writes_bytes_(BufferSize) PVOID Buffer, _In_ SIZE_T BufferSize,
//    _Out_opt_ PSIZE_T NumberOfBytesRead) {
//  const auto original = DdimonpFindOrignal(DdimonpHandleNtWriteVirtualMemory);
//  const auto result = original(ProcessHandle, BaseAddress, Buffer, BufferSize,
//                               NumberOfBytesRead);
//  HYPERPLATFORM_LOG_INFO_SAFE("hello");
//  HYPERPLATFORM_LOG_INFO_SAFE(
//      "DdimonpHandleNtReadVirtualMemory(Process Handle=%08x NumberOfBytesRead=
//      %08Ix,Bytes = 0x%p\n", ProcessHandle, NumberOfBytesRead, Buffer);
//  return result;
//}

_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtCreateProcess(
    _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_ HANDLE ParentProcess,
    _In_ BOOLEAN InheritObjectTable, _In_opt_ HANDLE SectionHandle,
    _In_opt_ HANDLE DebugPort, _In_opt_ HANDLE ExceptionPort) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtCreateProcess);
  const auto result =
      original(ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess,
               InheritObjectTable, SectionHandle, DebugPort, ExceptionPort);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtCreateProcess,ProcessHandle=%08x,Name=%wZ", ProcessHandle,
      ObjectAttributes->ObjectName);
  return result;
}

static NTSTATUS NTAPI DdimonpHandleNtOpenProcess(
    _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtOpenProcess);
  const auto result =
      original(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtOpenProcess,ProcessHandle=%08x,Name=%wZ,ClientId=%08x",
      ProcessHandle, ObjectAttributes->ObjectName, ClientId->UniqueProcess);
  return result;
}
_Use_decl_annotations_ static NTSTATUS DdimonpHandleMmCopyVirtualMemory(
    PEPROCESS SourceProcess, PVOID SourceAddress, PEPROCESS TargetProcess,
    PVOID TargetAddress, SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode,
    PSIZE_T ReturnSize) {
  const auto original = DdimonpFindOrignal(DdimonpHandleMmCopyVirtualMemory);
  const auto result =
      original(SourceProcess, SourceAddress, TargetProcess, TargetAddress,
               BufferSize, PreviousMode, ReturnSize);
  const auto SourcePid = PsGetProcessId(SourceProcess);
  const auto TargetPid = PsGetProcessId(TargetProcess);
  // HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleMmCopyVirtualMemory Status=
  // 0x%08X",
  //                            (NTSTATUS)result);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleMmCopyVirtualMemory,SourceProcess=%08x,SourcePID=%08x,"
      "TargetPID=%08x,BufferSize=%08x,ReturnSize=%08x,PreviousMode=%08x",
      SourceProcess, SourcePid, TargetPid, BufferSize, ReturnSize,
      PreviousMode);

  // auto buffer = reinterpret_cast<UCHAR*>(ExAllocatePoolWithTag(
  //    PagedPool, BufferSize, kHyperPlatformCommonPoolTag));
  // RtlZeroMemory(buffer, BufferSize);
  // if (buffer == NULL) {
  //  return result;
  //}
  // RtlCopyMemory(buffer, SourceAddress, BufferSize);

  ////const auto result2 =original(SourceProcess, SourceAddress,
  ///PsGetCurrentProcess(), buffer, 4, PreviousMode, ReturnSize);
  ////HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleMmCopyVirtualMemory
  ///Status=0x%08X",(NTSTATUS)result2);
  // HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleMmCopyVirtualMemory After Copy
  // Buffer=%0X", buffer[0]); ExFreePoolWithTag(buffer,
  // kHyperPlatformCommonPoolTag);
  return result;
}

_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtCreateSection(
    _Out_ PHANDLE SectionHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
    _In_opt_ PLARGE_INTEGER MaximumSize, _In_ ULONG SectionPageProtection,
    _In_ ULONG AllocationAttributes, _In_opt_ HANDLE FileHandle) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtCreateSection);
  const auto result =
      original(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize,
               SectionPageProtection, AllocationAttributes, FileHandle);
  POBJECT_NAME_INFORMATION filename;
  filename = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(
      NonPagedPool, MAXSIZE, kHyperPlatformCommonPoolTag);
  if (FileHandle) {
    ULONG currentProcessId;
    currentProcessId = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
    ZwQueryObject(FileHandle, (OBJECT_INFORMATION_CLASS)1, filename, MAXSIZE,
                  NULL);
    HYPERPLATFORM_LOG_INFO_SAFE(
        "DdimonpHandleNtCreateSection,File=%wZ,ProcessId=0x%08x,DesiredAccess="
        "0x%08x,SectionPageProtection=0x%08x,AllocationAttributes=0x%08x",
        &(filename->Name), currentProcessId, DesiredAccess,
        SectionPageProtection, AllocationAttributes);
  }
  return result;
}

_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtQueryInformationToken(
    _In_ HANDLE TokenHandle, _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
    _Out_writes_bytes_(TokenInformationLength) PVOID TokenInformation,
    _In_ ULONG TokenInformationLength, _Out_ PULONG ReturnLength) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleNtQueryInformationToken);
  const auto result =
      original(TokenHandle, TokenInformationClass, TokenInformation,
               TokenInformationLength, ReturnLength);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtQueryInformationToken,TokenHandle=%08x", TokenHandle);
  return result;
}

_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtSetInformationToken(
    _In_ HANDLE TokenHandle, _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
    _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
    _In_ ULONG TokenInformationLength) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtSetInformationToken);
  const auto result = original(TokenHandle, TokenInformationClass,
                               TokenInformation, TokenInformationLength);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtQueryInformationToken,TokenHandle=%08x", TokenHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtWriteFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_reads_bytes_(Length) PVOID Buffer,
    _In_ ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset,
    _In_opt_ PULONG Key) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtWriteFile);
  const auto result = original(FileHandle, Event, ApcRoutine, ApcContext,
                               IoStatusBlock, Buffer, Length, ByteOffset, Key);

  return result;
}

static NTSTATUS DdimonpHandleCmRegisterCallback(
    IN PEX_CALLBACK_FUNCTION Function, IN PVOID Context,
    IN OUT PLARGE_INTEGER Cookie) {
  const auto original = DdimonpFindOrignal(DdimonpHandleCmRegisterCallback);
  const auto result = original(Function, Context, Cookie);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleCmRegisterCallback");
  return result;
}

static NTSTATUS DdimonpHandleCmRegisterCallbackEx(
    IN PEX_CALLBACK_FUNCTION Function, IN PCUNICODE_STRING Altitude,
    IN PVOID Driver, IN PVOID Context, OUT PLARGE_INTEGER Cookie,
    PVOID Reserved) {
  const auto original = DdimonpFindOrignal(DdimonpHandleCmRegisterCallbackEx);
  const auto result =
      original(Function, Altitude, Driver, Context, Cookie, Reserved);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleCmRegisterCallbackEx");
  return result;
}

static NTSTATUS DdimonpHandleNtDeleteFile(
    _In_ POBJECT_ATTRIBUTES ObjectAttributes) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtDeleteFile);
  const auto result = original(ObjectAttributes);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtDeleteFile,File=%wZ",
                              ObjectAttributes->ObjectName);
  return result;
}

static NTSTATUS DdimonpHandleNtQueryDirectoryFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID FileInformation, _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass,
    _In_ BOOLEAN ReturnSingleEntry, _In_opt_ PUNICODE_STRING FileName,
    _In_ BOOLEAN RestartScan) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtQueryDirectoryFile);
  const auto result = original(
      FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation,
      Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtQueryDirectoryFile,Handle=%08x",
                              FileHandle);
  if (FileName != NULL) {
    HYPERPLATFORM_LOG_INFO_SAFE(
        "DdimonpHandleNtQueryDirectoryFile,Handle=%08x,FileName=%wZ",
        FileHandle, FileName);
  }
  return result;
}

static NTSTATUS DdimonpHandleNtReadFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length,
    _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtReadFile);
  const auto result = original(FileHandle, Event, ApcRoutine, ApcContext,
                               IoStatusBlock, Buffer, Length, ByteOffset, Key);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtReadFile,Handle=%08x",
                              FileHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtSetInformationFile(
    _In_ HANDLE FileHandle, _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_reads_bytes_(Length) PVOID FileInformation, _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtSetInformationFile);
  const auto result = original(FileHandle, IoStatusBlock, FileInformation,
                               Length, FileInformationClass);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtSetInformationFile,Handle=%08x",
                              FileHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtDeviceIoControlFile(
    _In_ HANDLE FileHandle, _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_ ULONG IoControlCode,
    _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
    _In_ ULONG InputBufferLength,
    _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer,
    _In_ ULONG OutputBufferLength) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtDeviceIoControlFile);
  const auto result = original(
      FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode,
      InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtDeviceIoControlFile,Handle=%08x",
                              FileHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtAdjustPrivilegesToken(
    _In_ HANDLE TokenHandle, _In_ BOOLEAN DisableAllPrivileges,
    _In_opt_ PTOKEN_PRIVILEGES NewState, _In_ ULONG BufferLength,
    _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength)
        PTOKEN_PRIVILEGES PreviousState,
    _Out_ _When_(PreviousState == NULL, _Out_opt_) PULONG ReturnLength) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleNtAdjustPrivilegesToken);
  const auto result = original(TokenHandle, DisableAllPrivileges, NewState,
                               BufferLength, PreviousState, ReturnLength);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtAdjustPrivilegesToken,Handle=%08x", TokenHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtDuplicateToken(
    _In_ HANDLE ExistingTokenHandle, _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_ BOOLEAN EffectiveOnly,
    _In_ TOKEN_TYPE TokenType, _Out_ PHANDLE NewTokenHandle) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtDuplicateToken);
  const auto result =
      original(ExistingTokenHandle, DesiredAccess, ObjectAttributes,
               EffectiveOnly, TokenType, NewTokenHandle);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtDuplicateToken,Handle=%08x",
                              ExistingTokenHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtAllocateVirtualMemory(
    _In_ HANDLE ProcessHandle,
    _Inout_ _At_(*BaseAddress,
                 _Readable_bytes_(*RegionSize) _Writable_bytes_(*RegionSize)
                     _Post_readable_byte_size_(*RegionSize)) PVOID* BaseAddress,
    _In_ ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize,
    _In_ ULONG AllocationType, _In_ ULONG Protect) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleNtAllocateVirtualMemory);
  const auto result = original(ProcessHandle, BaseAddress, ZeroBits, RegionSize,
                               AllocationType, Protect);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtAllocateVirtualMemory,ProcessHandle=%08x", ProcessHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtOpenProcessToken(_In_ HANDLE ProcessHandle,
                                                _In_ ACCESS_MASK DesiredAccess,
                                                _Out_ PHANDLE TokenHandle) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtOpenProcessToken);
  const auto result = original(ProcessHandle, DesiredAccess, TokenHandle);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleNtOpenProcessToken,ProcessHandle=%08x", ProcessHandle);
  return result;
}

static NTSTATUS DdimonpHandleNtOpenThreadToken(_In_ HANDLE ThreadHandle,
                                               _In_ ACCESS_MASK DesiredAccess,
                                               _In_ BOOLEAN OpenAsSelf,
                                               _Out_ PHANDLE TokenHandle) {
  const auto original = DdimonpFindOrignal(DdimonpHandleNtOpenThreadToken);
  const auto result =
      original(ThreadHandle, DesiredAccess, OpenAsSelf, TokenHandle);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleNtOpenThreadToken");
  return result;
}

static NTSTATUS DdimonpHandleRtlCreateRegistryKey(_In_ ULONG RelativeTo,
                                                  _In_ PWSTR Path) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlCreateRegistryKey);
  const auto result = original(RelativeTo, Path);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlCreateRegistryKey,Path=%S",
                              Path);
  return result;
}

static NTSTATUS DdimonpHandleRtlQueryRegistryValuesEx(
    _In_ ULONG RelativeTo, _In_ PWSTR Path,
    _In_ PRTL_QUERY_REGISTRY_TABLE QueryTable, _In_ PVOID Context,
    _In_opt_ PVOID Environment) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleRtlQueryRegistryValuesEx);
  const auto result =
      original(RelativeTo, Path, QueryTable, Context, Environment);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlCreateRegistryKey,Path=%S",
                              Path);
  return result;
}

static NTSTATUS DdimonpHandleRtlWriteRegistryValue(
    _In_ ULONG RelativeTo, _In_ PWSTR Path, _In_ PWSTR ValueName,
    _In_ ULONG ValueType, _In_ PVOID ValueData, _In_ ULONG ValueLength) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlWriteRegistryValue);
  const auto result =
      original(RelativeTo, Path, ValueName, ValueType, ValueData, ValueLength);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlWriteRegistryValue,Path=%S,ValueName=%S",Path,ValueName);
  return result;
}

static NTSTATUS DdimonpHandleRtlDeleteRegistryValue(_In_ ULONG RelativeTo,
                                                    _In_ PWSTR Path,
                                                    _In_ PWSTR ValueName) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlDeleteRegistryValue);
  const auto result = original(RelativeTo, Path, ValueName);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlDeleteRegistryValue,Path=%S",Path);
  return result;
}

static NTSTATUS DdimonpHandleRtlRtlCheckRegistryKey(_In_ ULONG RelativeTo,
                                                    _In_ PWSTR Path) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlRtlCheckRegistryKey);
  const auto result = original(RelativeTo, Path);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlRtlCheckRegistryKey,Path=%S",Path);
  return result;
}

static NTSTATUS DdimonpHandlePsCreateSystemThread(
    PHANDLE ThreadHandle, ULONG DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
    PCLIENT_ID ClientId, PKSTART_ROUTINE StartRoutine, PVOID StartContext) {
  const auto original = DdimonpFindOrignal(DdimonpHandlePsCreateSystemThread);
  const auto result =
      original(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle,
               ClientId, StartRoutine, StartContext);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandlePsCreateSystemThread");
  return result;
}
static NTSTATUS DdimonpHandlePsSetContextThread(
    __in PETHREAD Thread, __in PCONTEXT ThreadContext,
    __in KPROCESSOR_MODE PreviousMode) {
  const auto original = DdimonpFindOrignal(DdimonpHandlePsSetContextThread);
  const auto result = original(Thread, ThreadContext, PreviousMode);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandlePsSetContextThread");
  return result;
}

static NTSTATUS DdimonpHandlePsSuspendProcess(PEPROCESS pEProcess) {
  const auto original = DdimonpFindOrignal(DdimonpHandlePsSuspendProcess);
  const auto result = original(pEProcess);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandlePsSuspendProcess");
  return result;
}

static NTSTATUS DdimonpHandlePsResumeProcess(PEPROCESS pEProcess) {
  const auto original = DdimonpFindOrignal(DdimonpHandlePsResumeProcess);
  const auto result = original(pEProcess);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandlePsResumeProcess");
  return result;
}

static BOOLEAN DdimonpHandleSeAccessCheck(
    PSECURITY_DESCRIPTOR SecurityDescriptor,
    PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
    BOOLEAN SubjectContextLocked, ACCESS_MASK DesiredAccess,
    ACCESS_MASK PreviouslyGrantedAccess, PPRIVILEGE_SET* Privileges,
    PGENERIC_MAPPING GenericMapping, KPROCESSOR_MODE AccessMode,
    PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus) {
  const auto original = DdimonpFindOrignal(DdimonpHandleSeAccessCheck);
  const auto result =
      original(SecurityDescriptor, SubjectSecurityContext, SubjectContextLocked,
               DesiredAccess, PreviouslyGrantedAccess, Privileges,
               GenericMapping, AccessMode, GrantedAccess, AccessStatus);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleSeAccessCheck");
  return result;
}

static NTSTATUS DdimonpHandleRtlDecompressBuffer(
    _In_ USHORT CompressionFormat,
    _Out_writes_bytes_to_(UncompressedBufferSize, *FinalUncompressedSize)
        PUCHAR UncompressedBuffer,
    _In_ ULONG UncompressedBufferSize,
    _In_reads_bytes_(CompressedBufferSize) PUCHAR CompressedBuffer,
    _In_ ULONG CompressedBufferSize, _Out_ PULONG FinalUncompressedSize) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlDecompressBuffer);
  const auto result =
      original(CompressionFormat, UncompressedBuffer, UncompressedBufferSize,
               CompressedBuffer, CompressedBufferSize, FinalUncompressedSize);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlDecompressBuffer");
  return result;
}
static NTSTATUS DdimonpHandleRtlDecompressBufferEx(
    _In_ USHORT CompressionFormat,
    _Out_writes_bytes_to_(UncompressedBufferSize, *FinalUncompressedSize)
        PUCHAR UncompressedBuffer,
    _In_ ULONG UncompressedBufferSize,
    _In_reads_bytes_(CompressedBufferSize) PUCHAR CompressedBuffer,
    _In_ ULONG CompressedBufferSize, _Out_ PULONG FinalUncompressedSize,
    _In_ PVOID WorkSpace) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlDecompressBufferEx);
  const auto result = original(
      CompressionFormat, UncompressedBuffer, UncompressedBufferSize,
      CompressedBuffer, CompressedBufferSize, FinalUncompressedSize, WorkSpace);
  HYPERPLATFORM_LOG_INFO_SAFE("DdimonpHandleRtlDecompressBufferEx");
  return result;
}

static NTSTATUS DdimonpHandleIoCreateSymbolicLink(
    PUNICODE_STRING SymbolicLinkName, PUNICODE_STRING DeviceName) {
  const auto original = DdimonpFindOrignal(DdimonpHandleIoCreateSymbolicLink);
  const auto result = original(SymbolicLinkName, DeviceName);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleIoCreateSymbolicLink,SymbolicLinkName=%wZ,DeviceName=%wZ",
      SymbolicLinkName, DeviceName);
  return result;
}
static NTSTATUS DdimonpHandleRtlAnsiStringToUnicodeString(
    PUNICODE_STRING DestinationString, PCANSI_STRING SourceString,
    BOOLEAN AllocateDestinationString) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleRtlAnsiStringToUnicodeString);
  const auto result =
      original(DestinationString, SourceString, AllocateDestinationString);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleRtlAnsiStringToUnicodeString,DestinationString=%wZ",
      DestinationString);
  return result;
}
static NTSTATUS DdimonpHandleRtlUnicodeStringToAnsiString(
    PANSI_STRING DestinationString, PCUNICODE_STRING SourceString,
    BOOLEAN AllocateDestinationString) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleRtlUnicodeStringToAnsiString);
  const auto result =
      original(DestinationString, SourceString, AllocateDestinationString);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleRtlUnicodeStringToAnsiString,DestinationString=%Z",
      DestinationString);
  return result;
}

static VOID DdimonpHandleRtlInitUnicodeString(
    PUNICODE_STRING DestinationString, __drv_aliasesMem PCWSTR SourceString) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlInitUnicodeString);
  original(DestinationString, SourceString);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleRtlInitUnicodeString,DestinationString=%wZ",
      DestinationString);
}
static LONG DdimonpHandleRtlCompareUnicodeString(PCUNICODE_STRING String1,
                                                 PCUNICODE_STRING String2,
                                                 BOOLEAN CaseInSensitive) {
  const auto original =
      DdimonpFindOrignal(DdimonpHandleRtlCompareUnicodeString);
  const auto result = original(String1, String2, CaseInSensitive);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleRtlCompareUnicodeString,DestinationString=%wZ,SourceString="
      "%wZ",
      String1, String2);
  return result;
}
static VOID DdimonpHandleRtlInitAnsiString(PANSI_STRING DestinationString,
                                           __drv_aliasesMem PCSZ SourceString) {
  const auto original = DdimonpFindOrignal(DdimonpHandleRtlInitAnsiString);
  original(DestinationString, SourceString);
  HYPERPLATFORM_LOG_INFO_SAFE(
      "DdimonpHandleRtlInitAnsiString,DestinationString=%Z", DestinationString);
}